﻿// Imports
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Nickel
{

    /// <summary>
    /// Reads data
    /// </summary>
    public class Read : Page
    {

        /// <summary>
        /// Article list
        /// </summary>
        private List<Article> articles = null;

        /// <summary>
        /// Selected article
        /// </summary>
        private Article article = null;

        /// <summary>
        /// Author to activate
        /// </summary>
        private Author authorToActivate;

        /// <summary>
        /// Page end
        /// </summary>
        private int intEnd;

        /// <summary>
        /// Page number
        /// </summary>
        private int intPage;

        /// <summary>
        /// Page start
        /// </summary>
        private int intStart;

        /// <summary>
        /// Output string
        /// </summary>
        private string strOutput;

        /// <summary>
        /// Signed in author
        /// </summary>
        private Author user;

        /// <summary>
        /// Handles page initialization
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        protected void Page_Load(object sender, EventArgs e)
        {

            // Always start off with images right aligned
            Markup.bAlignRight = true;

            // Read argument
            strArgument = Request.Form[Constants.INPUT_ARGUMENT] == null || Request.Form[Constants.INPUT_ARGUMENT].Trim().Length < 1 ? string.Empty : Request.Form[Constants.INPUT_ARGUMENT] as string;

            // Set page number and count
            int pageCount = 1;
            intPage = int.MinValue;
            int.TryParse(strArgument, out intPage);

            // Page specified using argument
            if (intPage > 0)
            {

                // Clear argument if it is a page number
                strArgument = string.Empty;

            }
            else
            {

                // Second argument specified
                if (Request.Form[Constants.INPUT_PAGE] != null && Request.Form[Constants.INPUT_PAGE].Trim().Length > 0) 
                    int.TryParse(Request.Form[Constants.INPUT_PAGE] as string, out intPage);

            }

            // Get signed in user
            user = Account.GetSignedInAuthor(Data, Request.Cookies[Settings.COOKIE_NAME]);

            // Declare output strings
            string strArticles = string.Empty;
            string strPages = "<a href=\"http://nickel.codeplex.com/\" target=\"_blank\"><img src=\"" +
                strBaseUrl + "images/nickel.png\" align=\"right\" width=\"39\" height=\"8\" " +
                "alt=\"Nickel\" title=\"nickel\" border=\"0\" /></a>&nbsp;";

            // An error occurred
            if (strError != string.Empty)
            {

                // Write error
                Response.Write("{ \"" + Constants.FIELD_ERROR + "\": \"" + StringHelper.JsonHtml(strError) + "\" }");

            }
            else
            {

                // Argument specified
                if (strArgument != string.Empty)
                {

                    // Use argument to find article list
                    articles = Data.GetArticlesByTag(strArgument);
                    if (articles == null) 
                        articles = Data.GetArticlesByAuthorName(strArgument);
                    if (articles == null) 
                        articles = Data.GetArticlesByAuthorId(strArgument);
                    if (articles == null)
                    {

                        // Use argument to find single article
                        article = Data.GetArticleByTitle(strArgument);
                        if (article == null) 
                            article = Data.GetArticleById(strArgument);

                        // Set article to null if it is unpublished and trying to be viewed by a non-admin
                        if (article != null && !article.Published && (user == null || !user.Admin)) 
                            article = null;

                        // No article found
                        if (article == null)
                        {

                            // Check if this is an author activation
                            authorToActivate = Data.GetAuthorByActivationKey(strArgument);

                            // Not activating anyone
                            if (authorToActivate == null)
                            {

                                // Write error
                                strError = Errors.AUTHOR_NOT_FOUND;

                            }
                            else
                            {

                                // Clear activation key
                                authorToActivate.ActivationKey = "";

                                // Save data
                                Data.Save();

                                // Send activation notice to user and admin
                                authorToActivate.SendActivationNotice(strBaseUrl);

                                // Write results
                                strArticles += "<p class=\"" + Constants.RESPONSE_ID + "\">" +
                                    "<h1>" + Titles.ACTIVATE + "</h1>Your account is now activated! " +
                                    "Please sign in using the form to the right or click <a href=\"" +
                                    strBaseUrl + "\">here</a> to go back to the home page.</p>";

                            }

                        }

                    }

                }
                else
                {

                    // Get all articles
                    articles = Data.Articles;

                }

                // Check article string for activation confirmation message
                if (strArticles == string.Empty)
                {

                    // Declare article count
                    int articleCount = 0;

                    // We have articles
                    if (articles != null)
                    {

                        // Not admin
                        if (user == null || !user.Admin)
                        {

                            // Declare list of published articles
                            List<Article> publishedArticles = new List<Article>();

                            // Add published articles to list
                            foreach (Article checkArticle in articles)
                                if (checkArticle.Published) 
                                    publishedArticles.Add(checkArticle);

                            // Copy published articles to list
                            articles = publishedArticles;
                            publishedArticles = null;

                        }

                        // Set article count
                        articleCount = articles.Count;

                    }

                    // Only one article
                    if (articles != null && articleCount == 1)
                    {

                        // Set article and clear articles
                        article = articles[0];
                        articles.Clear();
                        articles = null;

                    }

                    // Multiple articles
                    if (articles != null)
                    {

                        // Get page counts
                        pageCount = (int)Math.Ceiling((double)articleCount / (double)(Settings.ARTICLES_PER_PAGE));

                        // Clamp page number
                        if (intPage < 1) 
                            intPage = 1;
                        if (intPage > pageCount) 
                            intPage = pageCount;

                        // Get article range
                        intStart = (intPage - 1) * Settings.ARTICLES_PER_PAGE;
                        if (intStart < 0 || intStart > articleCount) 
                            intStart = 0;
                        intEnd = intStart + Settings.ARTICLES_PER_PAGE;
                        if (intEnd > articleCount) 
                            intEnd = articleCount;

                        // Iterate through articles
                        for (int i = intStart; i < intEnd; i++)
                        {

                            // Toggle image align
                            Markup.bAlignRight = !Markup.bAlignRight;

                            // Write article
                            strArticles += GetArticle(articles[i], true) + "<div>&nbsp;</div>";

                            // Separate articles
                            if (i < intEnd - 1) 
                                strArticles += "<hr />";

                        }

                        // Add forward slash to argument if specified
                        if (strArgument.Length > 0) 
                            strArgument += "/";

                        // Add page links
                        if (pageCount > 1) 
                            for (int i = 1; i <= pageCount; i++) 
                                strPages += "<a href=\"" + strBaseUrl + strArgument + i.ToString() + 
                                    "\" class=\"pageLink" + (i == intPage ? "Active" : "") + "\">" +
                                    i.ToString() + "</a>";

                    }
                    else if (article != null)
                    {

                        // Write the article
                        strArticles += GetArticle(article);

                        // Update title
                        strPageTitle = " . " + article.Title;

                    }
                    else
                    {

                        // Clear article string
                        strArticles = string.Empty;

                        // Set page count
                        pageCount = 1;

                    }

                }

                // Set default string if nothing returned
                if (strArticles.Trim().Length < 1) 
                    strArticles = Errors.NO_RESULTS;

                // Write response
                Response.Write("{ \"" + Constants.FIELD_SUCCESS + "\": \"" + StringHelper.JsonHtml(strArticles) + "\", " +
                    "\"" + Constants.FIELD_TITLE + "\": \"" + StringHelper.JsonHtml(Settings.SITE_TITLE + strPageTitle + " . " + Settings.SITE_TAG_LINE) + "\", " +
                    "\"" + Constants.FIELD_PAGE + "\": \"" + StringHelper.JsonHtml(strPages) + "\"}");

            }

        }

        /// <summary>
        /// Writes article
        /// </summary>
        /// <param name="article">The article</param>
        /// <returns>HTML of article</returns>
        private string GetArticle(Article article)
        {

            // Call override with default truncate flag
            return GetArticle(article, false);

        }

        /// <summary>
        /// Writes article
        /// </summary>
        /// <param name="article">The article</param>
        /// <param name="truncate">True to show truncated version</param>
        /// <returns>HTML of article</returns>
        private string GetArticle(Article article, bool truncate)
        {

            // Initialize HTML
            string strArticle = string.Empty;

            // Get title
            string strTitle = article.Title;

            // Link title if truncating
            if (truncate) 
                strTitle = "<a href=\"" + strBaseUrl + StringHelper.Encode(article.Title) + "\" class=\"titleLink\">" + article.Title + "</a>";

            // Set meta deta
            strMetaDescription += " " + article.Description;
            strMetaKeywords += ", " + article.Keywords.Join(", ");

            // Start article with title and subtitle
            strArticle += "<h1>" + strTitle + "</h1><div>";

            // Check for photo
            bool bPhoto = Settings.PROFILE_IMAGE_ENABLED && !truncate && File.Exists(Server.MapPath(@"~/uploads/profile/" + article.Author + ".jpg"));

            // Add profile image
            if (bPhoto) 
                strArticle += "<div style=\"float:left;\"><img src=\"" + strBaseUrl + "uploads/profile/" + article.Author + ".jpg\" align=\"left\" style=\"margin-right:10px;\" /></div>";

            // Get author's name and home page
            Author author = Data.GetAuthorById(article.Author);
            string strAuthorName = author.Name;
            string strHomePageUrl = author.HomePageUrl;

            // Add author name and modified date
            strArticle += "<div style=\"text-align:left;height:" +
                (bPhoto ? Settings.PROFILE_IMAGE_SIZE.ToString() + "px" : "auto") +
                ";\"><h4>" + article.SubTitle + "</h4>";

            // Add author name
            strArticle += strHomePageUrl != string.Empty ? "<a href=\"" + strHomePageUrl + "\" class=\"tiny\" target=\"_blank\">" + strAuthorName + "</a>" : "<span class=\"tiny\">" + strAuthorName + "</span>";

            // Add date time stamp
            strArticle += "<span class=\"tiny\"> / Posted: " + article.CreationDateTime + (article.CreationDateTime == article.ModifiedDateTime ? "" : " / Updated: " + article.ModifiedDateTime) + "</span>";

            // Comments are enabled for this article
            if (article.CommentsEnabled)
            {

                // Define comment count string
                string strCommentCount = article.Comments.Count.ToString() + " " + (article.Comments.Count == 1 ? "comment" : "comments");

                // This is the list version...
                if (truncate)
                {

                    // Just show comment count
                    strArticle += "<span class=\"tiny\"> / " + strCommentCount + "</span>";

                }
                else
                {

                    // Assign ID on article page so it can be updated when comment count changes
                    strArticle += "<span class=\"tiny\"> / </span><span id=\"" +
                    Constants.TARGET_COMMENT_COUNT_ARTICLE + "\" name=\"" +
                    Constants.TARGET_COMMENT_COUNT_ARTICLE + "\" class=\"tiny\">" + strCommentCount + "</span>";

                }

            }

            // Check for signed in user
            if (user != null)
            {

                // Add edit and delete links for admin
                if (user.Admin) strArticle += "<span class=\"tiny\"> | </span><a onclick=\"onClick('" +
                    Constants.SOURCE_LINK + "', '" + Constants.TYPE_ARTICLE + "', '" + article.ID +
                    "');\" class=\"tiny\">Edit</a><span class=\"tiny\"> | </span><a onclick=\"onClick('" +
                    Constants.SOURCE_LINK + "', '" + Constants.TYPE_ARTICLE + "', '" + article.ID +
                    "', true);\" class=\"tiny\">Delete</a>";

                // Full view...
                if (!truncate)
                {

                    // Add un/subscribe link
                    bool bSubscribed = false;
                    foreach (string subscriberID in article.Subscriptions) 
                        if (subscriberID == user.ID && user.Active)
                            bSubscribed = true;
                    strArticle += "<span class=\"tiny\"> | </span><span id=\"" + Constants.TARGET_SUBSCRIBE + "\" name=\"" + Constants.TARGET_SUBSCRIBE + "\"><a onclick=\"onClick('" +
                        Constants.SOURCE_LINK + "', '" + Constants.TYPE_SUBSCRIBE + "', '" + article.ID +
                        "', " + (!bSubscribed).ToString().ToLower() + ");\" class=\"tiny\">" + (bSubscribed ? "Uns" : "S") + "ubscribe</a></span>";

                }

            }

            // Close author line
            strArticle += "</div>";

            // Article is in list
            if (truncate) 
                strArticle += Markup.FindImage(article.Text);

            // Write text or description (if truncating)
            strArticle += "</div><div class=\"articleText\">" + (truncate ? article.Description : Markup.Parse(article.Text)) + "</div>";

            // Article has tags
            int tagCount = article.Tags.Count;
            if (tagCount > 0)
            {

                // Start tag string
                strArticle += "<span class=\"tiny\">Tags: </span>";

                // Iterate through tags
                for (int i = 0; i < tagCount; i++)
                {

                    // Write tag link
                    strArticle += "<a href=\"" + strBaseUrl +
                        StringHelper.Encode(article.Tags[i]) +
                        "\" class=\"tiny\">" + StringHelper.ToTitleCase(article.Tags[i]) +
                        "</a>";

                    // Add comma where needed
                    if (i < tagCount - 1) strArticle += "<span class=\"tiny\">, </span>";

                }

                // Close tags
                strArticle += "</span>";

            }

            // Attach comments
            if (!truncate && article.CommentsEnabled) 
                strArticle += GetComments(article);

            // Write the article
            return strArticle;

        }

        /// <summary>
        /// Write comments
        /// </summary>
        /// <param name="article">Parent article</param>
        /// <returns>HTML of comments</returns>
        private string GetComments(Article article)
        {

            // Initialize return string
            string strComments = "<hr /><div id=\"" + Constants.TARGET_COMMENTS + "\"><h4 id=\"" + Constants.TARGET_COMMENT_COUNT + "\" name=\"" + Constants.TARGET_COMMENT_COUNT + "\">" +
                article.Comments.Count.ToString() + " comment" + (article.Comments.Count == 1 ? "" : "s") + "</h4><hr />";

            // Add hidden article ID field for submitting new comments
            strComments += "<input type=\"hidden\" id=\"" + Constants.INPUT_ARTICLE_ID +
                "\" name=\"" + Constants.INPUT_ARTICLE_ID +
                "\" value=\"" + article.ID + "\" />";

            // Add comment form
            strComments += "<div id=\"" + Constants.TARGET_COMMENT_FORM + "\"></div>" +
                "<div id=\"" + Constants.TARGET_COMMENT_ERROR + "\"></div>";

            // Sort and iterate through comments
            article.Comments.Sort(Sort.CommentByDate);
            foreach (Comment comment in article.Comments) 
                strComments += comment.Html(Data, user, GetCommentProfileImage(comment));

            // Return string
            return strComments + "</div>";

        }

        /// <summary>
        /// Write page title
        /// </summary>
        private string Title
        {

            get
            {

                // Initialize title
                string strTitle = Settings.SITE_TITLE;

                // Write page title
                if (strPageTitle.Length > 0) 
                    strTitle += " . " + strPageTitle;

                // Close title
                return strTitle + " . " + Settings.SITE_TAG_LINE;

            }

        }

    }

}
